#include "../include/Dictionary.h"
#include<fstream>
#include<sstream>
using namespace std;
Dictionary *Dictionary::_ptr = Dictionary::getInstance();

Dictionary *Dictionary::getInstance()
{
    if(!_ptr)
    {
        _ptr = new Dictionary();
    }
    return _ptr;
}

void Dictionary::destroy()
{
    if(_ptr)
    {
        delete _ptr;
        _ptr = nullptr;
    }
}

void Dictionary::init(const string &dictEn,const string &dictCn,const string &indexEn,const string &indexCn)
{
    size_t offset = readDict(dictEn);
    readDict(dictCn);
    readIndex(indexEn,0);
    readIndex(indexCn,offset);
}

string Dictionary::doQuery(string word)
{
    string cutResult;
    size_t charNums = length(word);
    for(size_t idx = 0, n = 0; n != charNums; ++idx, ++n) {
        size_t charLen = nBytesCode(word[idx]);
        string subWord = word.substr(idx, charLen);
        idx += (charLen - 1);
        cutResult += subWord;
        cutResult += " ";
    }
    return cutResult;
}
    
map<string,set<int>> &Dictionary::getIndex()
{
    return _index;
}

vector<pair<string,int>> &Dictionary::getDict()
{
    return _dict;
}

//--------------------------------------------------------------------------




size_t Dictionary::readDict(const string path)
{
    fstream fread(path,ios::in);
    if(!fread.good())return 0;
    string lines;
    while(getline(fread,lines))
    {
        stringstream ss;
        ss<<lines;
        int cnt = 2;
        pair<string,int> pir;
        while(ss && cnt>0)
        {
            string word;
            ss>>word;
            if(cnt == 2)
            {
                pir.first = word;
            }
            else if(cnt == 1)
            {
                pir.second = atoi(word.data());
            }
            --cnt;
        }
        _dict.push_back(pir);
    }
    fread.close();
    return _dict.size();
}

void Dictionary::readIndex(const string path,size_t offset)
{
    fstream fread(path,ios::in);
    if(!fread.good())return ;
    string lines;
    while(getline(fread,lines))
    {
        stringstream ss;
        ss<<lines;
        string word;
        ss>>word;
        pair<string,set<int>> pir;
        pir.first = word;
        while(ss)
        {
            string word;
            ss>>word;
            if(word.size()>0)
            {
                int nu = atoi(word.data());
                nu+=offset;
                pir.second.insert(nu);
            }
        }
        _index.insert(pir);
    }
    fread.close();
}

size_t Dictionary::nBytesCode(const char ch)
{
    if (ch & (1 << 7)) {
        int nBytes = 1;
        for (int idx = 0; idx != 6; ++idx) {
            if (ch & (1 << (6 - idx))) {
                ++nBytes;
            }
            else {
                break;
            }
        }
        return nBytes;
    }
    return 1;
}

size_t Dictionary::length(const string &str)
{
    std::size_t ilen = 0;
    for (std::size_t idx = 0; idx != str.size(); ++idx) {
        int nBytes = nBytesCode(str[idx]);
        idx += (nBytes - 1);
        ++ilen;
    }
    return ilen;
}

int Dictionary::triple_min(const int &a, const int &b, const int &c)
{
    return a < b ? (a < c ? a : c) : (b < c ? b : c);
}

int Dictionary::editDistance(const std::string & lhs, const std::string &rhs)
{
    // 计算最小编辑距离-包括处理中英文
    size_t lhs_len = length(lhs);
    size_t rhs_len = length(rhs);
    int editDist[lhs_len + 1][rhs_len + 1];
    for (size_t idx = 0; idx <= lhs_len; ++idx) {
        editDist[idx][0] = idx;
    }
    for (size_t idx = 0; idx <= rhs_len; ++idx) {
        editDist[0][idx] = idx;
    }
    std::string sublhs, subrhs;
    for (std::size_t dist_i = 1, lhs_idx = 0; dist_i <= lhs_len; ++dist_i, ++lhs_idx) {
        size_t nBytes = nBytesCode(lhs[lhs_idx]);
        sublhs = lhs.substr(lhs_idx, nBytes);
        lhs_idx += (nBytes - 1);
        for (std::size_t dist_j = 1, rhs_idx = 0;
             dist_j <= rhs_len; ++dist_j, ++rhs_idx) {
            nBytes = nBytesCode(rhs[rhs_idx]);
            subrhs = rhs.substr(rhs_idx, nBytes);
            rhs_idx += (nBytes - 1);
            if (sublhs == subrhs) {
                editDist[dist_i][dist_j] = editDist[dist_i - 1][dist_j - 1];
            }
            else {
                editDist[dist_i][dist_j] = triple_min(editDist[dist_i][dist_j - 1] + 1, editDist[dist_i - 1][dist_j] + 1, editDist[dist_i - 1][dist_j - 1] + 1);
            }
        }
    }
    return editDist[lhs_len][rhs_len];
}
